/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2023. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */

/*!
 * @file ac780x_can.c
 *
 * @brief This file provides CAN integration functions.
 *
 */

/* ===========================================  Includes  =========================================== */
#include "ac780x_can_reg.h"

/* ============================================  Define  ============================================ */
#define CAN_IRQ_FLAG_MSK              (0x00D5FF00UL)    /* All interrupt flag mask macro */
#define CAN_ECC_IRQ_FLAG_MSK          (0x00140000UL)    /* Memory ECC interrupt flag mask macro */
#define CAN_ECC_IRQ_FLAG_OFFSET       (8UL)             /* CAN memory ECC interrupt flag offset */
#define CAN_ECC_INIT_TIMEOUT          (100UL)           /* CAN memory ECC initialization timeout */
#define CAN_MAX_WAIT_TIMES            (100000UL)        /* default wait times, or user define */

#define CAN_DATA_WRITE(ADDR, DATA, TEMPBUF) \
    { ADDR = DATA; \
      while(1) { \
        TEMPBUF[0] = ADDR; \
        TEMPBUF[1] = ADDR; \
        if (TEMPBUF[0] == DATA && TEMPBUF[1] == DATA) break; \
        ADDR = DATA; }}

#define CAN_DATA_READ(ADDR, DATA, TEMPBUF) \
    { while(1) { \
        TEMPBUF[0] = ADDR; \
        TEMPBUF[1] = ADDR; \
        TEMPBUF[2] = ADDR; \
        if (TEMPBUF[0] == TEMPBUF[1] && TEMPBUF[1] == TEMPBUF[2]) break; } \
        DATA = TEMPBUF[0]; }

/* ===========================================  Typedef  ============================================ */
/* CAN device information struct */
typedef struct
{
    uint32_t               interruptFlag;              /* CAN interrupt flag */
    uint32_t               waitTimes;                  /* Wait times in the polling mode */
    ACTION_Type            interruptEn;                /* CAN interrupt enable */
    ACTION_Type            timeStampEn;                /* Time stamp enable */
    ACTION_Type            memEccEn;                   /* CAN memory ECC enable */
    CAN_TransmitAmountType tsAmount;                   /* Transmit secondary all frames or one frame */
    DeviceCallback_Type    callback;                   /* CAN callback pointer */
} CAN_DeviceType;

/* CAN system information struct */
typedef struct
{
    CAN_Type *const     canBase;                      /* CAN module base address */
    IRQn_Type           irq;                          /* CAN interrupt number */
    CKGEN_ClockType     clock;                        /* clock */
    CKGEN_SoftResetType reset;                        /* reset */
} CAN_InfoType;

/* ==========================================  Variables  =========================================== */
/* CAN device information */
static const CAN_InfoType s_canInfo[CAN_INSTANCE_MAX] =
{
    {CAN0, CAN0_IRQn, CLK_CAN0, SRST_CAN0},
    {CAN1, CAN1_IRQn, CLK_CAN1, SRST_CAN1},
};

static CAN_DeviceType s_canDevice[CAN_INSTANCE_MAX] = {0};

/* ======================================  Functions define  ======================================== */
/*!
 * @brief Get CAN module base address.
 *
 * @param[in] instance: CAN module instance
 * @return CAN module base address
 */
CAN_Type* CAN_GetBase(uint8_t instance)
{
    DEVICE_ASSERT(instance < CAN_INSTANCE_MAX);

    return (s_canInfo[instance].canBase);
}

/*!
 * @brief Set call back function.
 *
 * @param[in] CANx: CAN type pointer
 * @param[in] callbackFunc: Event call back function
 */
void CAN_SetCallBack(CAN_Type *CANx, DeviceCallback_Type callbackFunc)
{
    uint8_t instance = CAN_INDEX(CANx);

    DEVICE_ASSERT(instance < CAN_INSTANCE_MAX);

    s_canDevice[instance].callback = callbackFunc;
}

/*!
 * @brief Enable or disable CAN interrupt.
 *
 * @param[in] CANx: CAN module
 * @param[in] enable: interrupt enable state
 * @return none
 */
static void CAN_EnableIRQ(CAN_Type *CANx, ACTION_Type enable)
{
    uint8_t instance = CAN_INDEX(CANx);

    DEVICE_ASSERT(instance < CAN_INSTANCE_MAX);

    if (enable)
    {
        NVIC_EnableIRQ(s_canInfo[instance].irq);
    }
    else
    {
        NVIC_DisableIRQ(s_canInfo[instance].irq);
        NVIC_ClearPendingIRQ(s_canInfo[instance].irq);
    }
    s_canDevice[instance].interruptEn = enable;
}

/*!
 * @brief CANx interrupt handler function.
 *
 * @param[in] CANx: CAN module
 * @return none
 */
void CAN_HandleEvent(CAN_Type *CANx)
{
    uint8_t instance = CAN_INDEX(CANx);
    CAN_DeviceType *device = &s_canDevice[instance];
    uint32_t flag = CAN_GetCtrl1(CANx);

    DEVICE_ASSERT(instance < CAN_INSTANCE_MAX);

    device->interruptFlag = (flag & CAN_IRQ_FLAG_MSK);
    /* Clear flag */
    CAN_SetCtrl1(CANx, flag);
    /* Read and clear memory ECC error flag */
    if (device->memEccEn)
    {
        flag = CAN_GetVerMem(CANx);
        CAN_SetVerMem(CANx, flag);
        device->interruptFlag |= (flag & CAN_ECC_IRQ_FLAG_MSK) << CAN_ECC_IRQ_FLAG_OFFSET;
    }
    /* Call back function */
    if (device->callback != NULL)
    {
        device->callback(CANx, device->interruptFlag, (uint32_t)CAN_GetKoer(CANx));
    }
}

/*!
 * @brief CAN0 interrupt handle function.
 *
 * @param[in] none
 * @return none
 */
void CAN0_Handler(void)
{
    CAN_HandleEvent(CAN0);
}

/*!
 * @brief CAN1 interrupt handle function.
 *
 * @param[in] none
 * @return none
 */
void CAN1_Handler(void)
{
    CAN_HandleEvent(CAN1);
}

/*!
 * @brief Set CAN bitrate.
 *
 * @param[in] CANx: CAN module
 * @param[in] config: Bitrate config
 * @param[in] dataBitrate: Is data bit rate 0: is not data bit rate, 1: is data bitrate
 * @return none
 */
static void CAN_SetBitrate(CAN_Type *CANx, const CAN_BitrateConfigType *bitrate, uint8_t dataBitrate)
{
    DEVICE_ASSERT(bitrate != NULL);
    ACTION_Type reset = DISABLE;

    /* Check the RESET is 1 first */
    reset = (ACTION_Type)CAN_GetResetState(CANx);
    if (DISABLE == reset)
    {
        CAN_SetReset(CANx, ENABLE);
    }
    if (!dataBitrate)
    {
        CAN_SetSlowBitrate(CANx, bitrate);
    }
    else
    {
        CAN_SetFastBitrate(CANx, bitrate);
    }

    if (DISABLE == reset)
    {
        CAN_SetReset(CANx, DISABLE);
    }
}

/*!
 * @brief Initialize CAN module.
 *
 * @param[in] CANx: CAN module
 * @param[in] config: CAN config
 * @return status 0: success 1: error
 */
uint8_t CAN_Init(CAN_Type *CANx, CAN_ConfigType *config)
{
    uint8_t ret = 0U, i = 0U;
    CAN_FilterControlType *filterList = NULL;
    uint8_t instance = CAN_INDEX(CANx);
    CAN_DeviceType *device = &s_canDevice[instance];

    DEVICE_ASSERT(instance < CAN_INSTANCE_MAX);
    DEVICE_ASSERT(config != NULL);

    device->interruptFlag = 0U;

    /* CAN and clock enable */
    CKGEN_SetCANClock(instance, (CAN_ClockSelectType)config->clockSrc);
    CKGEN_Enable(s_canInfo[instance].clock, ENABLE);
    CKGEN_SoftReset(s_canInfo[instance].reset, ENABLE);

    /* Reset CAN */
    CAN_SetReset(CANx, ENABLE);

    /* Set memory ECC */
    CAN_SetMemEcc(CANx, config->memEccEn);
    device->memEccEn = config->memEccEn;

    /* Set bitrate */
    CAN_SetBitrate(CANx, config->normalBitrate, 0U);
    if (config->fdModeEn)
    {
        CAN_SetFdIso(CANx, config->fdIsoEn);
        CAN_SetBitrate(CANx, config->dataBitrate, 1U);
        CAN_SetTdc(CANx, config->tdcEnable);
        CAN_SetSspOffet(CANx, config->sspOffset);
    }
    /* Wait memory ECC initialization done */
    if (config->memEccEn)
    {
        while (0U == CAN_IsMemInitDone(CANx))
        {
            if (++i > CAN_ECC_INIT_TIMEOUT)
            {
                return 1;
            }
        }
    }
    /* Set filters(user define or use sample setting) */
    if (config->filterList)
    {
        filterList = (CAN_FilterControlType *)config->filterList;
        for (i = 0U; i < config->filterNum; i++)
        {
            CAN_SetFilter(CANx, filterList[i].index, filterList[i].code, filterList[i].mask, filterList[i].enable);
        }
    }

    /* Start synchronize */
    CAN_SetReset(CANx, DISABLE);

    /* Set work mode */
    if (CAN_MODE_MONITOR == config->canMode)
    {
        CAN_SetListenOnlyMode(CANx, ENABLE);
    }
    else if (CAN_MODE_LOOPBACK_INTERNAL == config->canMode)
    {
        CAN_SetLoopbackIntMode(CANx, ENABLE);
    }
    else if (CAN_MODE_LOOPBACK_EXTERNAL == config->canMode)
    {
        CAN_SetLoopbackExtMode(CANx, ENABLE);
        CAN_SetSack(CANx, config->selfAckEn);
    }
    else
    {
    }
    if (config->rbFullWarningLimit)
    {
        CAN_SetAfwl(CANx, config->rbFullWarningLimit);
    }
    else
    {
        CAN_SetAfwl(CANx, (uint8_t)(CAN_RECEIVE_FIFO_COUNT - 1U));
    }
    CAN_SetEwl(CANx, config->errorWarningLimit);
    CAN_SetTpss(CANx, config->tpss);
    CAN_SetTsss(CANx, config->tsss);

    CAN_SetTSMode(CANx, config->tsMode); /* 0->FIFO mode 1->priority decision mode */
    CAN_SetRom(CANx, config->rom);       /* 0->overwrite the oldest message 1->discard new message */
    CAN_SetBusOffRecDisable(CANx, config->busOffRecDisable);
    device->tsAmount = config->tsAmount;
    device->waitTimes = config->waitTimes ? config->waitTimes : CAN_MAX_WAIT_TIMES;

    /* Set CAN time stamp */
    CAN_EnableTimeStamp(CANx, DISABLE);
    if (config->timeStampEn)
    {
        CKGEN_SetCANTimeDivider(instance, config->timeStampClk);
        device->timeStampEn = config->timeStampEn;
        CAN_SetTimePosition(CANx, config->timeStampPos);
        CAN_EnableTimeStamp(CANx, ENABLE);
        CAN_SetTimeStampCount(CANx, ENABLE);
    }
    CAN_SetCallBack(CANx, config->callback);
    CAN_SetIntEnable(CANx, config->interruptMask);
    CAN_SetEccIntMask(CANx, config->memEccIntMask);

    /* Enable interrupt */
    CAN_EnableIRQ(CANx, config->interruptEn);

    return ret;
}

/*!
 * @brief Uninitialize CAN module.
 *
 * @param[in] CANx: CAN module
 * @return none
 */
void CAN_DeInit(CAN_Type *CANx)
{
    uint8_t instance = CAN_INDEX(CANx);

    DEVICE_ASSERT(instance < CAN_INSTANCE_MAX);

    CAN_SetReset(CANx, ENABLE);

    /* CAN and clock disable */
    CAN_EnableIRQ(CANx, DISABLE);
    CAN_SetCallBack(CANx, NULL);
    CKGEN_SoftReset(s_canInfo[instance].reset, DISABLE);
    CKGEN_Enable(s_canInfo[instance].clock, DISABLE);
}

/*!
 * @brief Wait transmit primary done, otherwise timeout.
 *
 * @param[in] CANx: CAN module
 * @return Transmit flag (0: done, 1: timeout)
 */
int32_t WaitTransmitPrimaryDone(CAN_Type *CANx)
{
    int32_t ret = 0;
    uint32_t i = 0U;
    uint8_t instance = CAN_INDEX(CANx);

    do
    {
        if (CANx->CTRL1 & CAN_CTRL1_TPIF_Msk)
        {
            CANx->CTRL1 |= CAN_CTRL1_TPIF_Msk;
            break;
        }
    } while (i++ < s_canDevice[instance].waitTimes);

    if (i >= s_canDevice[instance].waitTimes)
    {
        ret = 1;
    }

    return ret;
}

/*!
 * @brief Wait transmit secondary done, otherwise timeout.
 *
 * @param[in] CANx: CAN module
 * @return Transmit flag (0: done, 1: timeout)
 */
int32_t WaitTransmitSecondaryDone(CAN_Type *CANx)
{
    int32_t ret = 0;
    uint32_t i = 0U;
    uint8_t instance = CAN_INDEX(CANx);

    do
    {
        if (CANx->CTRL1 & CAN_CTRL1_TSIF_Msk)
        {
            CANx->CTRL1 |= CAN_CTRL1_TSIF_Msk;
            break;
        }
    } while (i++ < s_canDevice[instance].waitTimes);

    if (i >= s_canDevice[instance].waitTimes)
    {
        ret = 1;
    }

    return ret;
}

/*!
 * @brief Wait transmit done.
 *
 * @param[in] CANx: CAN module
 * @param[in] type: CAN transmit type
 *              - CAN_TRANSMIT_PRIMARY
 *              - CAN_TRANSMIT_SECONDARY
 * @return Transmit flag (0: done, 1: timeout)
 */
int32_t CAN_WaitTransmissionDone(CAN_Type *CANx, CAN_TransmitBufferType type)
{
    int32_t ret = 0;
    uint8_t instance = CAN_INDEX(CANx);

    DEVICE_ASSERT(instance < CAN_INSTANCE_MAX);

    if (s_canDevice[instance].interruptEn)
    {
        /* clear flag in IRQ */
    }
    else if (CAN_TRANSMIT_PRIMARY == type)
    {
        ret = WaitTransmitPrimaryDone(CANx);
    }
    else
    {
        ret = WaitTransmitSecondaryDone(CANx);
    }

    return ret;
}

/*!
 * @brief Wait for transmitting done, otherwise timeout.
 *
 * @param[in] CANx: CAN module
 * @param[in] type: CAN transmit type
 *              - CAN_TRANSMIT_PRIMARY
 *              - CAN_TRANSMIT_SECONDARY
 * @return Transmit idle flag (0: idle, 1: timeout)
 */
int32_t CAN_WaitTransmissionIdle(CAN_Type *CANx, CAN_TransmitBufferType type)
{
    int32_t ret = 0;
    uint32_t i = 0U;
    uint8_t instance = CAN_INDEX(CANx);

    do
    {
        if (!CAN_IsTransmitting(CANx, type))
        {
            break;
        }
    } while (i++ < s_canDevice[instance].waitTimes);

    if (i >= s_canDevice[instance].waitTimes)
    {
        ret = 1;
    }

    return ret;
}

/*!
 * @brief Get payload size.
 *
 * @param[in] dlcValue: DLC value
 * @return payload size
 */
uint8_t CAN_GetPayloadSize(uint8_t dlcValue)
{
    uint8_t ret = 0U;

    if (dlcValue <= 8U)
    {
        ret = dlcValue;
    }
    else
    {
        switch (dlcValue)
        {
        case CAN_DLC_12_BYTES:
            ret = 12U;
            break;

        case CAN_DLC_16_BYTES:
            ret = 16U;
            break;

        case CAN_DLC_20_BYTES:
            ret = 20U;
            break;

        case CAN_DLC_24_BYTES:
            ret = 24U;
            break;

        case CAN_DLC_32_BYTES:
            ret = 32U;
            break;

        case CAN_DLC_48_BYTES:
            ret = 48U;
            break;

        case CAN_DLC_64_BYTES:
            ret = 64U;
            break;

        default:
            break;
        }
    }

    return ret;
}

/*!
 * @brief Get message DLC value.
 *
 * @param[in] payloadSize: payload size
 * @return DLC value
 */
uint8_t CAN_GetDlcValue(uint8_t payloadSize)
{
    uint8_t dlcValue = 0U;

    if (payloadSize <= 8U)
    {
        dlcValue = payloadSize;
    }
    else if (payloadSize <= 12U)
    {
        dlcValue = (uint8_t)CAN_DLC_12_BYTES;
    }
    else if (payloadSize <= 16U)
    {
        dlcValue = (uint8_t)CAN_DLC_16_BYTES;
    }
    else if (payloadSize <= 20U)
    {
        dlcValue = (uint8_t)CAN_DLC_20_BYTES;
    }
    else if (payloadSize <= 24U)
    {
        dlcValue = (uint8_t)CAN_DLC_24_BYTES;
    }
    else if (payloadSize <= 32U)
    {
        dlcValue = (uint8_t)CAN_DLC_32_BYTES;
    }
    else if (payloadSize <= 48U)
    {
        dlcValue = (uint8_t)CAN_DLC_48_BYTES;
    }
    else if (payloadSize <= 64U)
    {
        dlcValue = (uint8_t)CAN_DLC_64_BYTES;
    }
    else
    {
    }

    return dlcValue;
}

/*!
 * @brief Set message information.
 *
 * @param[in] CANx: CAN module
 * @param[in] info: CAN message information
 * @param[in] type: CAN transmit type
 *              - CAN_TRANSMIT_PRIMARY
 *              - CAN_TRANSMIT_SECONDARY
 * @return none
 */
void CAN_SetMsgInfo(CAN_Type *CANx, const CAN_MsgInfoType *info, CAN_TransmitBufferType type)
{
    uint8_t dataLength = 0, i = 0, tempIndex = 0;
    uint32_t tempControl = 0, tempId = 0, tempData = 0;
    uint8_t instance = CAN_INDEX(CANx);
    uint32_t tempBuf[2] = {0};

    DEVICE_ASSERT(instance < CAN_INSTANCE_MAX);

    /* Select transmit buffer */
    if (CAN_TRANSMIT_PRIMARY == type)
    {
        CANx->CTRL0 &= (~(CAN_CTRL0_TBSEL_Msk | CAN_CTRL0_TPE_Msk | CAN_CTRL0_TSNEXT_Msk | CAN_CTRL0_TSONE_Msk | CAN_CTRL0_TSALL_Msk));
    }
    else
    {
        CANx->CTRL0 = ((CANx->CTRL0 & (~(CAN_CTRL0_TPE_Msk | CAN_CTRL0_TSNEXT_Msk | CAN_CTRL0_TSONE_Msk | CAN_CTRL0_TSALL_Msk))) | CAN_CTRL0_TBSEL_Msk);
    }
    /* Set message information */
    tempId = (info->ID & CAN_INFO_ID_Msk);

    /* Set TTSEN bit */
    if (s_canDevice[instance].timeStampEn)
    {
        tempId |= CAN_INFO_TTSEN_Msk;
    }
    /* Set ESI bit */
    if (info->ESI)
    {
        tempId |= CAN_INFO_ESI_Msk;
    }

    CAN_DATA_WRITE(CANx->TBUF.ID_ESI, tempId, tempBuf);

    /* Set message control bits */
    tempControl = (uint32_t)(info->DLC & CAN_INFO_DLC_Msk);
    if (info->BRS)
    {
        tempControl |= CAN_INFO_BRS_Msk;
    }
    if (info->FDF)
    {
        tempControl |= CAN_INFO_FDF_Msk;
    }
    else if (info->RTR)
    {
        tempControl |= CAN_INFO_RTR_Msk;
    }
    if (info->IDE)
    {
        tempControl |= CAN_INFO_IDE_Msk;
    }

    CAN_DATA_WRITE(CANx->TBUF.TXCTRL, tempControl, tempBuf);


    /* Set message data bits */
    if ((0U == info->RTR) && info->DLC && info->DATA)
    {
        dataLength = CAN_GetPayloadSize(info->DLC);
        if ((uint32_t)info->DATA % 4U)
        {
            for (i = 0U; i < dataLength; i += 4U)
            {
                tempData = info->DATA[i] | (info->DATA[i + 1U] << 8U) |
                           (info->DATA[i + 2U] << 16U) | (info->DATA[i + 3U] << 24U);
                tempIndex = (i >> 2U);
                CAN_DATA_WRITE(CANx->TBUF.DATA[tempIndex], tempData, tempBuf);
            }
        }
        else
        {
            for (i = 0U; i < dataLength; i += 4U)
            {
                tempData = *(uint32_t *)(&info->DATA[i]);
                tempIndex = (i >> 2U);
                CAN_DATA_WRITE(CANx->TBUF.DATA[tempIndex], tempData, tempBuf);
            }
        }
    }

    /* Point to secondary next buffer */
    if (CAN_TRANSMIT_SECONDARY == type)
    {
        CANx->CTRL0 = (CANx->CTRL0 & (~(CAN_CTRL0_TSALL_Msk | CAN_CTRL0_TSONE_Msk | CAN_CTRL0_TPE_Msk))) | CAN_CTRL0_TSNEXT_Msk;
    }
}

/*!
 * @brief Set transmit amount for can secondary buffer.
 *
 * @param[in] CANx: CAN module
 * @param[in] amount: CAN transmit secondary amount
 *              - CAN_TRANSMIT_ALL
 *              - CAN_TRANSMIT_ONE
 * @return none
 */
void CAN_SetTransmitAmount(CAN_Type *CANx, CAN_TransmitAmountType amount)
{
    uint8_t instance = CAN_INDEX(CANx);

    DEVICE_ASSERT(instance < CAN_INSTANCE_MAX);

    s_canDevice[instance].tsAmount = amount;
}

/*!
 * @brief Transmit CAN message.
 *
 * @param[in] CANx: CAN module
 * @param[in] info: CAN message information
 * @param[in] type: CAN transmit type
 *              - CAN_TRANSMIT_PRIMARY
 *              - CAN_TRANSMIT_SECONDARY
 * @return 0: success, -1: error, -2: busy, -3: timeout, -4: Transmit timeout
 */
int32_t CAN_TransmitMessage(CAN_Type *CANx, const CAN_MsgInfoType *info, CAN_TransmitBufferType type)
{
    int32_t ret = 0;
    uint8_t instance = CAN_INDEX(CANx);
    CAN_DeviceType *device = &s_canDevice[instance];

    DEVICE_ASSERT(instance < CAN_INSTANCE_MAX);
    DEVICE_ASSERT(info != NULL);

    /* Check CAN is in the reset or standby status */
    if (CAN_GetResetState(CANx) || CAN_GetStandbyState(CANx))
    {
        return -1;
    }

    if (device->interruptEn)
    {
        /* Check transmission is idle or the TBUF is not full, when transmitting secondary all */
        if ((CAN_TRANSMIT_SECONDARY == type) && (CAN_TRANSMIT_ALL == device->tsAmount))
        {
            if (CAN_IsTransmitBusy(CANx, type))
            {
                ret = -2;
            }
        }
        else if (CAN_IsTransmitting(CANx, type))
        {
            ret = -2;
        }
    }
    else
    {
        if (CAN_WaitTransmissionIdle(CANx, type))
        {
            ret = -3;
        }
    }

    if (0 == ret)
    {
        CAN_SetMsgInfo(CANx, info, type);
        CAN_StartTransmission(CANx, type, device->tsAmount);
        if (CAN_WaitTransmissionDone(CANx, type))
        {
            ret = -4;
        }
    }

    return ret;
}

/*!
 * @brief Receive CAN message.
 *
 * @param[in] CANx: CAN module
 * @param[in] info: CAN message information
 * @return 0: success, 1: no message
 */
int32_t CAN_ReceiveMessage(CAN_Type *CANx, CAN_MsgInfoType *info)
{
    int32_t ret = 0;
    uint8_t dataLength = 0, i = 0, tempIndex = 0;
    uint32_t tempBuf[3] = {0};
    uint32_t tempData = 0;

    if (!CAN_GetRStat(CANx))
    {
        ret = 1;
    }
    else
    {
        /* Read message information */
        CAN_DATA_READ(CANx->RBUF.ID_ESI, tempData, tempBuf);
        info->ID  = tempData & CAN_INFO_ID_Msk;
        info->ESI = (uint8_t)(tempData  >> CAN_INFO_ESI_Pos);
        CAN_DATA_READ(CANx->RBUF.RXCTRL, tempData, tempBuf);
        info->BRS = (uint8_t)((tempData >> CAN_INFO_BRS_Pos) & 0x01U);
        info->FDF = (uint8_t)((tempData >> CAN_INFO_FDF_Pos) & 0x01U);
        info->RTR = (uint8_t)((tempData >> CAN_INFO_RTR_Pos) & 0x01U);
        info->IDE = (uint8_t)((tempData >> CAN_INFO_IDE_Pos) & 0x01U);
        info->DLC = (uint8_t)(tempData & CAN_INFO_DLC_Msk);

        if ((0U == info->RTR) && info->DLC && info->DATA)
        {
            dataLength = CAN_GetPayloadSize(info->DLC);
            if ((0U == info->FDF) && (dataLength > 8U))
            {
                dataLength = 8U;
            }
            if (((uint32_t)info->DATA % 4U) || (dataLength < 8U))
            {
                for (i = 0U; i < dataLength;)
                {
                    tempIndex = i/4;
                    CAN_DATA_READ(CANx->RBUF.DATA[tempIndex], tempData, tempBuf);

                    for(tempIndex = 0; tempIndex < 4 && i < dataLength; tempIndex++, i++)
                    {
                        info->DATA[i] = ((uint8_t *)&tempData)[tempIndex];
                    }
                }
            }
            else
            {
                for (i = 0U; i < dataLength; i += 4U)
                {
                    tempIndex = (i >> 2U);
                    CAN_DATA_READ(CANx->RBUF.DATA[tempIndex], *(uint32_t *)(&info->DATA[i]), tempBuf);
                }
            }
        }

        CAN_DATA_READ(CANx->RBUF.RTS[0], info->RTS, tempBuf);
        /* Receive buffer release */
        CANx->CTRL0 = ((CANx->CTRL0 & (~(CAN_CTRL0_TPE_Msk | CAN_CTRL0_TSNEXT_Msk | CAN_CTRL0_TSONE_Msk | CAN_CTRL0_TSALL_Msk))) | CAN_CTRL0_RREL_Msk);
    }

    return ret;
}

/*!
 * @brief Set CAN filter.
 *
 * @param[in] CANx: CAN module
 * @param[in] index: CAN filter index
 *              - (0-15)
 * @param[in] code: CAN filter code
 * @param[in] mask: CAN filter mask
 * @param[in] state: CAN filter enable state
 *              - ENABLE
 *              - DISABLE
 * @return none
 */
void CAN_SetFilter(CAN_Type *CANx, uint8_t index, uint32_t code, uint32_t mask, ACTION_Type state)
{
    ACTION_Type reset = DISABLE;

    DEVICE_ASSERT(index < CAN_MAX_FILTER_NUM);
    /* Check the RESET is 1 first */
    reset = (ACTION_Type)CAN_GetResetState(CANx);
    if (DISABLE == reset)
    {
        CAN_SetReset(CANx, ENABLE);
    }

    CAN_SetAcfEn(CANx, index, state);
    if (state)
    {
        CAN_SetAcfIndex(CANx, index);
        CAN_SetAcfCode(CANx, code);
        CAN_SetAcfMask(CANx, mask);
    }

    if (DISABLE == reset)
    {
        CAN_SetReset(CANx, DISABLE);
    }
}

/*!
 * @brief Get interrupt flags.
 *
 * @param[in] CANx: CAN module
 * @return interrupt flag
 */
uint32_t CAN_GetInterruptFlag(CAN_Type *CANx)
{
    uint8_t instance = CAN_INDEX(CANx);
    uint32_t flag;

    DEVICE_ASSERT(instance < CAN_INSTANCE_MAX);

    if (s_canDevice[instance].interruptEn)
    {
        flag = s_canDevice[instance].interruptFlag;
    }
    else
    {
        flag = (CAN_GetCtrl1(CANx) & CAN_IRQ_FLAG_MSK) | \
               ((CAN_GetVerMem(CANx) & CAN_ECC_IRQ_FLAG_MSK) << CAN_ECC_IRQ_FLAG_OFFSET);
    }

    return flag;
}

/*!
 * @brief Clear interrupt flags.
 *
 * @param[in] CANx: CAN module
 * @return none
 */
void CAN_ClearInterruptFlag(CAN_Type *CANx)
{
    uint8_t instance = CAN_INDEX(CANx);

    DEVICE_ASSERT(instance < CAN_INSTANCE_MAX);

    if (s_canDevice[instance].interruptEn)
    {
        s_canDevice[instance].interruptFlag = 0U;
    }
    else
    {
        CAN_SetCtrl1(CANx, CAN_GetCtrl1(CANx));
        CAN_SetVerMem(CANx, CAN_GetVerMem(CANx));
    }
}

/* =============================================  EOF  ============================================== */
